package com.yanqu.road.utils.plot;

import java.util.*;

/**
 * 地图玩法工具类
 */
public class PlotUtil {

    public enum Direction {
        LEFT_DOWN, LEFT_UP, RIGHT_DOWN, RIGHT_UP
    }

    /**
     * 获取给定x和y坐标周围上下左右的4个地块
     *
     * @param x       起始点的x坐标
     * @param y       起始点的y坐标
     * @return 上下左右4个相邻地块的坐标列表
     */
    public static List<Point> getAdjacentPoints(int x, int y) {
        List<Point> adjacentPoints = new ArrayList<>();
        adjacentPoints.add(new Point(x - 1, y)); // 左
        adjacentPoints.add(new Point(x + 1, y)); // 右
        adjacentPoints.add(new Point(x, y - 1)); // 上
        adjacentPoints.add(new Point(x, y + 1)); // 下
        return adjacentPoints;
    }

    /**
     * 获取给定x和y坐标周围上下左右的4个地块
     *
     * @param x       起始点的x坐标
     * @param y       起始点的y坐标
     * @return 上下左右4个相邻地块的坐标列表
     */
    public static Set<String> getAdjacentPointSet(int x, int y) {
        Set<String> adjacentPoints = new HashSet<>();
        adjacentPoints.add((x - 1) + "_" + y); // 左
        adjacentPoints.add((x + 1) + "_" + y);// 右
        adjacentPoints.add((x) + "_" + (y - 1));// 上
        adjacentPoints.add((x) + "_" + (y + 1));// 下
        return adjacentPoints;
    }

    /**
     * 根据输入的坐标点，返回上方、右侧、右上斜方的三个坐标点组成的列表。
     *
     * @param x       起始点的x坐标
     * @param y       起始点的y坐标
     * @return 包含三个坐标点的列表
     */
    public static List<Point> getAdjacentCoordinates(int x, int y) {
        List<Point> coordinates = new ArrayList<>();

        // 上方的点
        coordinates.add(new Point(x, y + 1));

        // 右侧的点
        coordinates.add(new Point(x + 1, y));

        // 右上斜方的点
        coordinates.add(new Point(x + 1, y + 1));

        return coordinates;
    }

    /**
     * 根据输入的坐标点，下方、右侧、右下斜方的三个坐标点组成的列表。
     *
     * @param x       起始点的x坐标
     * @param y       起始点的y坐标
     * @return 包含三个坐标点的列表
     */
    public static List<Point> getAdjacentCoordinates2(int x, int y) {
        List<Point> coordinates = new ArrayList<>();

        // 下方的点
        coordinates.add(new Point(x, y - 1));

        // 右侧的点
        coordinates.add(new Point(x + 1, y));

        // 右下斜方的点
        coordinates.add(new Point(x + 1, y - 1));

        return coordinates;
    }

    /**
     * 计算并返回给定点周围的指定范围内的所有坐标点列表
     *
     * @param centerX   中心点的x坐标
     * @param centerY   中心点的y坐标
     * @param range      范围，表示距离中心点的距离
     * @return 包含范围内的所有坐标点的列表
     */
    public static List<Point> getPointsInRadius(int centerX, int centerY, int range) {
        List<Point> points = new ArrayList<>();
        for (int dx = -range; dx <= range; dx++) {
            for (int dy = -range; dy <= range; dy++) {
                if (dx == 0 && dy == 0) {
                    continue;
                }
                if (centerX + dx < 0 || centerY + dy < 0) {
                    continue;
                }
                points.add(new Point(centerX + dx, centerY + dy));
            }
        }
        return points;
    }

    /**
     * 判断给定的点（n, m）是否在以指定中心点（centerX, centerY）为圆心、半径为radius的圆范围内。
     *
     * @param centerX 圆心的x坐标。
     * @param centerY 圆心的y坐标。
     * @param radius 圆的半径。
     * @param n 待判断点的x坐标。
     * @param m 待判断点的y坐标。
     * @return 如果点（n, m）在圆内或圆上，则返回true；否则返回false。
     */
    public static boolean isWithinRange(int centerX, int centerY, int radius, int n, int m) {
        if (centerX == n && centerY == m) {
            return false;
        }
        int distance = Math.max(Math.abs(n - centerX), Math.abs(m - centerY));
        return distance <= radius;
    }

    /**
     * 检查给定的坐标点是否在地图的边界内
     *
     * @param mapWidth  地图的宽度
     * @param mapHeight 地图的高度
     * @param x         要检查的x坐标
     * @param y         要检查的y坐标
     * @return 如果坐标点在地图内，返回true，否则返回false
     */
    public static boolean isPointInMap(int mapWidth, int mapHeight, int x, int y) {
        return x >= 0 && x < mapWidth && y >= 0 && y < mapHeight;
    }

    /**
     * 计算两点之间的曼哈顿距离
     *
     * @param point1 第一个点的坐标
     * @param point2 第二个点的坐标
     * @return 两点之间的曼哈顿距离
     */
    public static int manhattanDistance(Point point1, Point point2) {
        return Math.abs(point1.getX() - point2.getX()) + Math.abs(point1.getY() - point2.getY());
    }

    /**
     * 计算两点之间的欧几里得距离
     *
     * @param point1 第一个点的坐标
     * @param point2 第二个点的坐标
     * @return 两点之间的欧几里得距离
     */
    public static double euclideanDistance(Point point1, Point point2) {
        return Math.sqrt(Math.pow(point1.getX() - point2.getX(), 2) + Math.pow(point1.getY() - point2.getY(), 2));
    }

    /**
     * 生成一个矩形区域内的所有坐标点。
     *
     * @param x 【左上角】横坐标
     * @param y 【左上角】纵坐标
     * @param k 长度
     * @param l 宽度
     * @return 包含所有坐标点的列表
     */
    public static List<int[]> generateRectangleCoordinates(int x, int y, int k, int l) {
        List<int[]> coordinates = new ArrayList<>();
        for (int i = x; i < x + k; i++) {
            for (int j = y; j < y + l; j++) {
                coordinates.add(new int[]{i, j});
            }
        }
        return coordinates;
    }

    /**
     * 生成一个矩形区域内的所有坐标点。
     *
     * @param x 【左下角】横坐标
     * @param y 【左下角】纵坐标
     * @param k 长度
     * @param l 宽度
     * @return 包含所有坐标点的列表
     */
    public static List<int[]> generateRectangleCoordinates2(int x, int y, int k, int l) {
        List<int[]> coordinates = new ArrayList<>();

        // 横坐标 i 从 x 开始递增到 x + k - 1
        for (int i = x; i < x + k; i++) {
            // 纵坐标 j 从 y 开始递增到 y + l - 1
            for (int j = y; j < y + l; j++) {
                coordinates.add(new int[]{i, j});
            }
        }

        return coordinates;
    }

    /**
     * 判断给定的点列表是否构成一个连在一起的正方形网格。
     *
     * @param points 点列表
     * @param size   正方形网格的边长
     * @return 如果构成正方形网格则返回 true，否则返回 false。
     */
    public static boolean isSquareGrid(List<Point> points, int size) {
        if (points == null || points.size() != size * size) {
            return false;
        }

        Set<Integer> xCoords = new HashSet<>();
        Set<Integer> yCoords = new HashSet<>();
        for (Point point : points) {
            xCoords.add(point.getX());
            yCoords.add(point.getY());
        }

        int minX = Collections.min(xCoords);
        int maxX = Collections.max(xCoords);
        int minY = Collections.min(yCoords);
        int maxY = Collections.max(yCoords);

        // 检查 x 和 y 的范围是否为 size
        if (maxX - minX != size - 1 || maxY - minY != size - 1) {
            return false;
        }

        // 检查所有可能的坐标组合是否都在集合中
        for (int x = minX; x <= maxX; x++) {
            for (int y = minY; y <= maxY; y++) {
                if (!xCoords.contains(x) || !yCoords.contains(y)) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * 判断是否是五个格组
     * @param points
     * @return
     */
    public static boolean isFivePoints(List<Point> points) {
        if (points == null || points.size() != 5) {
            return false;
        }
        //中心点
        Point centerPoint = points.remove(0);
        //中心掉的上下左右
        Set<String> set = getAdjacentPointSet(centerPoint.getX(), centerPoint.getY());
        for (Point point : points) {
            if (!set.contains(point.getKey())) {
                return false;
            }
        }
        return true;
    }

    /**
     * 向某个方向移动steps步，输出路过的所有坐标
     * @param x
     * @param y
     * @param steps
     * @param direction
     * @return
     */
    public static Map<String, PointFjd> expandPositions(int x, int y, int steps, Direction direction) {
        //结果MAP
        Map<String, PointFjd> resultMap = new HashMap<>();
        //起始点
        PointFjd center = new PointFjd(x, y, 0);
        resultMap.put(center.getKey(), center);
        //移动
        switch (direction) {
            case LEFT_DOWN:
                pointMoveLeftDown(center, steps, resultMap);
                break;
            case LEFT_UP:
                pointMoveLeftUp(center, steps, resultMap);
                break;
            case RIGHT_DOWN:
                pointMoveRightDown(center, steps, resultMap);
                break;
            case RIGHT_UP:
                pointMoveRightUp(center, steps, resultMap);
                break;
        }
        return resultMap;
    }

    /**
     * 左下移动
     * @param point
     * @param steps
     * @param resultMap
     */
    public static void pointMoveLeftDown(PointFjd point, int steps, Map<String, PointFjd> resultMap) {
        //终止条件
        if (point.getDistance() >= steps) {
            return;
        }

        //向左
        PointFjd left = new PointFjd(point.getX() - 1, point.getY(), point.getDistance() + 1);
        resultMap.put(left.getKey2(), left);
        //继续走
        pointMoveLeftDown(left, steps, resultMap);

        //向下
        PointFjd down = new PointFjd(point.getX(), point.getY() - 1, point.getDistance() + 1);
        resultMap.put(down.getKey2(), down);
        //继续走
        pointMoveLeftDown(down, steps, resultMap);
    }

    /**
     * 左上移动
     * @param point
     * @param steps
     * @param resultMap
     */
    public static void pointMoveLeftUp(PointFjd point, int steps, Map<String, PointFjd> resultMap) {
        //终止条件
        if (point.getDistance() >= steps) {
            return;
        }

        //向左
        PointFjd left = new PointFjd(point.getX() - 1, point.getY(), point.getDistance() + 1);
        resultMap.put(left.getKey2(), left);
        //继续走
        pointMoveLeftUp(left, steps, resultMap);

        //向上
        PointFjd up = new PointFjd(point.getX(), point.getY() + 1, point.getDistance() + 1);
        resultMap.put(up.getKey2(), up);
        //继续走
        pointMoveLeftUp(up, steps, resultMap);
    }

    /**
     * 右下移动
     * @param point
     * @param steps
     * @param resultMap
     */
    public static void pointMoveRightDown(PointFjd point, int steps, Map<String, PointFjd> resultMap) {
        //终止条件
        if (point.getDistance() >= steps) {
            return;
        }

        //向右
        PointFjd right = new PointFjd(point.getX() + 1, point.getY(), point.getDistance() + 1);
        resultMap.put(right.getKey2(), right);
        //继续走
        pointMoveRightDown(right, steps, resultMap);

        //向下
        PointFjd down = new PointFjd(point.getX(), point.getY() - 1, point.getDistance() + 1);
        resultMap.put(down.getKey2(), down);
        //继续走
        pointMoveRightDown(down, steps, resultMap);
    }

    /**
     * 右上移动
     * @param point
     * @param steps
     * @param resultMap
     */
    public static void pointMoveRightUp(PointFjd point, int steps, Map<String, PointFjd> resultMap) {
        //终止条件
        if (point.getDistance() >= steps) {
            return;
        }

        //向右
        PointFjd right = new PointFjd(point.getX() + 1, point.getY(), point.getDistance() + 1);
        resultMap.put(right.getKey2(), right);
        //继续走
        pointMoveRightUp(right, steps, resultMap);

        //向上
        PointFjd up = new PointFjd(point.getX(), point.getY() + 1, point.getDistance() + 1);
        resultMap.put(up.getKey2(), up);
        //继续走
        pointMoveRightUp(up, steps, resultMap);
    }


    public static void main(String[] args) {
        Map<String, PointFjd> map = expandPositions(29, 11, 3, Direction.RIGHT_UP);
        System.out.println(map);
    }
}
